/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.configuration; import java.io.File; import java.io.FileNotFoundException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; import java.math.BigDecimal; import java.math.BigInteger; import java.net.MalformedURLException; import java.net.URL; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.configuration.event.ConfigurationErrorEvent; import org.apache.commons.configuration.event.ConfigurationErrorListener; import org.apache.commons.configuration.event.ConfigurationEvent; import org.apache.commons.configuration.event.ConfigurationListener; import org.apache.commons.configuration.tree.ConfigurationNode; import org.apache.commons.configuration.tree.ExpressionEngine; /** * This class provides access to multiple configuration files that reside in a location that * can be specified by a pattern allowing applications to be multi-tenant. For example, * providing a pattern of "file:///opt/config/${product}/${client}/config.xml" will result in * "product" and "client" being resolved on every call. The configuration resulting from the * resolved pattern will be saved for future access. * @since 1.6 * @author <a * href="http://commons.apache.org/configuration/team-list.html">Commons * Configuration team</a> * @version $Id: MultiFileHierarchicalConfiguration.java 727958 2008-12-19 07:19:24Z oheger $ */ public class MultiFileHierarchicalConfiguration extends AbstractHierarchicalFileConfiguration implements ConfigurationListener, ConfigurationErrorListener { /** FILE URL prefix */ private static final String FILE_URL_PREFIX = "file:"; /** * Prevent recursion while resolving unprefixed properties. */ private static ThreadLocal recursive = new ThreadLocal() { protected synchronized Object initialValue() { return Boolean.FALSE; } }; /** Map of configurations */ private final Map configurationsMap = new HashMap(); /** key pattern for configurationsMap */ private String pattern; /** True if the constructor has finished */ private boolean init; /** * Default Constructor. */ public MultiFileHierarchicalConfiguration() { super(); this.init = true; } /** * Construct the configuration with the specified pattern. * @param pathPattern The pattern to use to locate configuration files. */ public MultiFileHierarchicalConfiguration(String pathPattern) { super(); this.pattern = pathPattern; this.init = true; } /** * Set the File pattern * @param pathPattern The pattern for the path to the configuration. */ public void setFilePattern(String pathPattern) { this.pattern = pathPattern; } /** * Creates the file configuration delegate, i.e. the object that implements * functionality required by the <code>FileConfiguration</code> interface. * This base implementation will return an instance of the * <code>FileConfigurationDelegate</code> class. Derived classes may * override it to create a different delegate object. * * @return the file configuration delegate */ protected FileConfigurationDelegate createDelegate() { return new FileConfigurationDelegate(); } public void addProperty(String key, Object value) { this.getConfiguration().addProperty(key, value); } public void clear() { this.getConfiguration().clear(); } public void clearProperty(String key) { this.getConfiguration().clearProperty(key); } public boolean containsKey(String key) { return this.getConfiguration().containsKey(key); } public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) { return this.getConfiguration().getBigDecimal(key, defaultValue); } public BigDecimal getBigDecimal(String key) { return this.getConfiguration().getBigDecimal(key); } public BigInteger getBigInteger(String key, BigInteger defaultValue) { return this.getConfiguration().getBigInteger(key, defaultValue); } public BigInteger getBigInteger(String key) { return this.getConfiguration().getBigInteger(key); } public boolean getBoolean(String key, boolean defaultValue) { return this.getConfiguration().getBoolean(key, defaultValue); } public Boolean getBoolean(String key, Boolean defaultValue) { return this.getConfiguration().getBoolean(key, defaultValue); } public boolean getBoolean(String key) { return this.getConfiguration().getBoolean(key); } public byte getByte(String key, byte defaultValue) { return this.getConfiguration().getByte(key, defaultValue); } public Byte getByte(String key, Byte defaultValue) { return this.getConfiguration().getByte(key, defaultValue); } public byte getByte(String key) { return this.getConfiguration().getByte(key); } public double getDouble(String key, double defaultValue) { return this.getConfiguration().getDouble(key, defaultValue); } public Double getDouble(String key, Double defaultValue) { return this.getConfiguration().getDouble(key, defaultValue); } public double getDouble(String key) { return this.getConfiguration().getDouble(key); } public float getFloat(String key, float defaultValue) { return this.getConfiguration().getFloat(key, defaultValue); } public Float getFloat(String key, Float defaultValue) { return this.getConfiguration().getFloat(key, defaultValue); } public float getFloat(String key) { return this.getConfiguration().getFloat(key); } public int getInt(String key, int defaultValue) { return this.getConfiguration().getInt(key, defaultValue); } public int getInt(String key) { return this.getConfiguration().getInt(key); } public Integer getInteger(String key, Integer defaultValue) { return this.getConfiguration().getInteger(key, defaultValue); } public Iterator getKeys() { return this.getConfiguration().getKeys(); } public Iterator getKeys(String prefix) { return this.getConfiguration().getKeys(prefix); } public List getList(String key, List defaultValue) { return this.getConfiguration().getList(key, defaultValue); } public List getList(String key) { return this.getConfiguration().getList(key); } public long getLong(String key, long defaultValue) { return this.getConfiguration().getLong(key, defaultValue); } public Long getLong(String key, Long defaultValue) { return this.getConfiguration().getLong(key, defaultValue); } public long getLong(String key) { return this.getConfiguration().getLong(key); } public Properties getProperties(String key) { return this.getConfiguration().getProperties(key); } public Object getProperty(String key) { return this.getConfiguration().getProperty(key); } public short getShort(String key, short defaultValue) { return this.getConfiguration().getShort(key, defaultValue); } public Short getShort(String key, Short defaultValue) { return this.getConfiguration().getShort(key, defaultValue); } public short getShort(String key) { return this.getConfiguration().getShort(key); } public String getString(String key, String defaultValue) { return this.getConfiguration().getString(key, defaultValue); } public String getString(String key) { return this.getConfiguration().getString(key); } public String[] getStringArray(String key) { return this.getConfiguration().getStringArray(key); } public boolean isEmpty() { return this.getConfiguration().isEmpty(); } public void setProperty(String key, Object value) { if (init) { this.getConfiguration().setProperty(key, value); } } public Configuration subset(String prefix) { return this.getConfiguration().subset(prefix); } public Node getRoot() { return this.getConfiguration().getRoot(); } public void setRoot(Node node) { if (init) { this.getConfiguration().setRoot(node); } else { super.setRoot(node); } } public ConfigurationNode getRootNode() { return this.getConfiguration().getRootNode(); } public void setRootNode(ConfigurationNode rootNode) { if (init) { this.getConfiguration().setRootNode(rootNode); } else { super.setRootNode(rootNode); } } public ExpressionEngine getExpressionEngine() { return super.getExpressionEngine(); } public void setExpressionEngine(ExpressionEngine expressionEngine) { super.setExpressionEngine(expressionEngine); } public void addNodes(String key, Collection nodes) { this.getConfiguration().addNodes(key, nodes); } public SubnodeConfiguration configurationAt(String key, boolean supportUpdates) { return this.getConfiguration().configurationAt(key, supportUpdates); } public SubnodeConfiguration configurationAt(String key) { return this.getConfiguration().configurationAt(key); } public List configurationsAt(String key) { return this.getConfiguration().configurationsAt(key); } public void clearTree(String key) { this.getConfiguration().clearTree(key); } public int getMaxIndex(String key) { return this.getConfiguration().getMaxIndex(key); } public Configuration interpolatedConfiguration() { return this.getConfiguration().interpolatedConfiguration(); } public void addConfigurationListener(ConfigurationListener l) { super.addConfigurationListener(l); } public boolean removeConfigurationListener(ConfigurationListener l) { return super.removeConfigurationListener(l); } public Collection getConfigurationListeners() { return super.getConfigurationListeners(); } public void clearConfigurationListeners() { super.clearConfigurationListeners(); } public void addErrorListener(ConfigurationErrorListener l) { super.addErrorListener(l); } public boolean removeErrorListener(ConfigurationErrorListener l) { return super.removeErrorListener(l); } public void clearErrorListeners() { super.clearErrorListeners(); } public Collection getErrorListeners() { return super.getErrorListeners(); } public void save(Writer writer) throws ConfigurationException { if (init) { this.getConfiguration().save(writer); } } public void load(Reader reader) throws ConfigurationException { if (init) { this.getConfiguration().load(reader); } } public void load() throws ConfigurationException { this.getConfiguration().load(); } public void load(String fileName) throws ConfigurationException { this.getConfiguration().load(fileName); } public void load(File file) throws ConfigurationException { this.getConfiguration().load(file); } public void load(URL url) throws ConfigurationException { this.getConfiguration().load(url); } public void load(InputStream in) throws ConfigurationException { this.getConfiguration().load(in); } public void load(InputStream in, String encoding) throws ConfigurationException { this.getConfiguration().load(in, encoding); } public void save() throws ConfigurationException { this.getConfiguration().save(); } public void save(String fileName) throws ConfigurationException { this.getConfiguration().save(fileName); } public void save(File file) throws ConfigurationException { this.getConfiguration().save(file); } public void save(URL url) throws ConfigurationException { this.getConfiguration().save(url); } public void save(OutputStream out) throws ConfigurationException { this.getConfiguration().save(out); } public void save(OutputStream out, String encoding) throws ConfigurationException { this.getConfiguration().save(out, encoding); } public void configurationChanged(ConfigurationEvent event) { if (event.getSource() instanceof XMLConfiguration) { Iterator iter = getConfigurationListeners().iterator(); while (iter.hasNext()) { ConfigurationListener listener = (ConfigurationListener) iter.next(); listener.configurationChanged(event); } } } public void configurationError(ConfigurationErrorEvent event) { if (event.getSource() instanceof XMLConfiguration) { Iterator iter = getErrorListeners().iterator(); while (iter.hasNext()) { ConfigurationErrorListener listener = (ConfigurationErrorListener) iter.next(); listener.configurationError(event); } } } /* * Don't allow resolveContainerStore to be called recursively. * @param key The key to resolve. * @return The value of the key. */ protected Object resolveContainerStore(String key) { if (((Boolean) recursive.get()).booleanValue()) { return null; } recursive.set(Boolean.TRUE); try { return super.resolveContainerStore(key); } finally { recursive.set(Boolean.FALSE); } } /** * Remove the current Configuration. */ public void removeConfiguration() { String path = getSubstitutor().replace(pattern); synchronized (configurationsMap) { configurationsMap.remove(path); } } /** * First checks to see if the cache exists, if it does, get the associated Configuration. * If not it will load a new Configuration and save it in the cache. * * @return the Configuration associated with the current value of the path pattern. */ private AbstractHierarchicalFileConfiguration getConfiguration() { if (pattern == null) { throw new ConfigurationRuntimeException("File pattern must be defined"); } String path = getSubstitutor().replace(pattern); synchronized (configurationsMap) { if (configurationsMap.containsKey(path)) { return (AbstractHierarchicalFileConfiguration) configurationsMap.get(path); } } if (path.equals(pattern)) { XMLConfiguration configuration = new XMLConfiguration() { public void load() throws ConfigurationException { } public void save() throws ConfigurationException { } }; synchronized (configurationsMap) { configurationsMap.put(pattern, configuration); } return configuration; } XMLConfiguration configuration = new XMLConfiguration(); try { URL url = getURL(path); configuration.setURL(url); configuration.load(); configuration.setExpressionEngine(getExpressionEngine()); configuration.setReloadingStrategy(getReloadingStrategy()); configuration.addConfigurationListener(this); configuration.addErrorListener(this); synchronized (configurationsMap) { if (!configurationsMap.containsKey(path)) { configurationsMap.put(path, configuration); } } } catch (ConfigurationException ce) { throw new ConfigurationRuntimeException(ce); } catch (FileNotFoundException fnfe) { throw new ConfigurationRuntimeException(fnfe); } return configuration; } private URL getURL(String resourceLocation) throws FileNotFoundException { if (resourceLocation == null) { throw new IllegalArgumentException("A path pattern must be configured"); } try { // try URL return new URL(resourceLocation); } catch (MalformedURLException ex) { // no URL -> treat as file path try { return new URL(FILE_URL_PREFIX + resourceLocation); } catch (MalformedURLException ex2) { throw new FileNotFoundException("Resource location [" + resourceLocation + "] is not a URL or a well-formed file path"); } } } }